package com.young.rpc.server;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @description：
 * @author: yangyahui01
 * @date: 11/25/24 3:37 PM
 */
public class DefaultRpcServerImpl implements RpcServer{

    static class NamedDaemonThreadFactory implements ThreadFactory {
        private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);

        private final int poolNumber = POOL_NUMBER.getAndIncrement();
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final ThreadFactory delegate;
        private final boolean daemon;

        NamedDaemonThreadFactory(ThreadFactory delegate, boolean daemon) {
            this.delegate = delegate;
            this.daemon = daemon;
        }

        public Thread newThread(Runnable r) {
            Thread t = delegate.newThread(r);
            t.setName(String.format("young-schedule-http-%d-%d", poolNumber, threadNumber.getAndIncrement()));
            t.setDaemon(daemon);
            return t;
        }

        static ThreadFactory defaultThreadFactory(boolean daemon) {
            return new NamedDaemonThreadFactory(Executors.defaultThreadFactory(), daemon);
        }
    }

    private final HttpServer httpServer;

    private final ExecutorService executorService;

    private final boolean daemon;


    public DefaultRpcServerImpl(HttpServer httpServer,boolean daemon) {
        if (httpServer.getAddress() == null) {
            throw new IllegalArgumentException("HttpServer hasn't been bound to an address");
        }

        this.daemon = daemon;

        this.httpServer = httpServer;

        this.executorService = Executors.newFixedThreadPool(5, NamedDaemonThreadFactory.defaultThreadFactory(daemon));

        httpServer.setExecutor(executorService);
    }

    private DefaultRpcServerImpl(InetSocketAddress addr, boolean daemon) throws IOException {
        this(HttpServer.create(addr, 3), daemon);
    }

    private DefaultRpcServerImpl(int port, boolean daemon) throws IOException {
        this(new InetSocketAddress(port), daemon);
    }


    public static Builder builder() {
        return new Builder();
    }

    static class Builder implements RpcServer.Builder{

        private boolean daemon = false;
        private int port;

        Map<String, HttpHandler> handlerMapping;

        public RpcServer.Builder daemon(boolean daemon) {
            this.daemon = daemon;
            return this;
        }

        public RpcServer.Builder port(int port) {
            this.port = port;
            return this;
        }

        public RpcServer.Builder handler(Map<String, HttpHandler> handlerMapping) {
            this.handlerMapping = handlerMapping;
            return this;
        }

        public RpcServer build() throws Exception {
            RpcServer defaultRpcServer = new DefaultRpcServerImpl(port, daemon);

            if(handlerMapping == null || handlerMapping.isEmpty()) {
                throw new IllegalArgumentException("rpc server 缺少url方法映射");
            }
            defaultRpcServer.handlerMapping(handlerMapping);
            return defaultRpcServer;
        }
    }


    public void start() {

        this.start(daemon);

    }

    public void start(boolean daemon) {
        if (daemon == Thread.currentThread().isDaemon()) {
            httpServer.start();
        } else {
            FutureTask<Void> startTask = new FutureTask<>(() -> httpServer.start(),null);
            NamedDaemonThreadFactory.defaultThreadFactory(daemon).newThread(startTask).start();
            try {
                startTask.get();
            } catch (ExecutionException e) {
                throw new RuntimeException("Unexpected exception on starting HTTPSever", e);
            } catch (InterruptedException e) {
                // This is possible only if the current tread has been interrupted,
                // but in real use cases this should not happen.
                // In any case, there is nothing to do, except to propagate interrupted flag.
                Thread.currentThread().interrupt();
            }
        }
    }

    public void stop() {
        httpServer.stop(0);
        executorService.shutdown(); // Free any (parked/idle) threads in pool
    }

    public boolean isAlive() {
        InetSocketAddress address = httpServer.getAddress();
        return !address.isUnresolved();
    }

    public int getPort() {
        return httpServer.getAddress().getPort();
    }

    public void handlerMapping(Map<String, HttpHandler> handlerMapping) {

        handlerMapping.forEach((url,httpHandler) -> {
            httpServer.createContext(url,httpHandler);
        });

    }
}
